Я пытаюсь понять, как именно электронная почта обрабатывается Postfix, и некоторые более тонкие детали почтовых транзакций SMTP. Моя краткосрочная цель - отладить проприетарный (двоичный, с закрытым исходным кодом) SMTP-клиент, но сначала я подумал, что изучу, что происходит в успешной SMTP-транзакции.
Я планирую заблокировать исходящий SMTP (порт 25) на нашем брандмауэре локальной сети, поэтому я настроил Postfix как внутренний почтовый сервер для приема почты от (примитивного) локального клиентского программного обеспечения, которое может отправлять электронную почту только через SMTP без аутентификации (через порт 25).
Включил отладку Постфикса smtpd
процесс, добавив -v
подробный флаг в master.cf
как описано в Устранение неполадок с журналами Postfix. Затем я отправил электронное письмо со своей рабочей станции, используя Cygwin Mutt с sSMTP (минимальная реализация sendmail
).
Журналы Postfix показывают, что после RCPT TO:
строка была успешно обработана, адрес получателя приемлем, Postfix smtpd
присвоил транзакции идентификатор очереди и ответил клиенту SMTP (sSMTP) с 250 OK
.
Однако вместо выдачи DATA
команда, клиент SMTP выдал RSET
чтобы сбросить / прервать текущую почтовую транзакцию, и Postfix ответил 250 OK
.
Я провел некоторое исследование того, что делает эта команда, и неудивительно, что Простой протокол передачи почты, RFC 2821 предоставили наиболее полную информацию:
Эта команда указывает, что текущая почтовая транзакция будет прервана. Любые сохраненные данные об отправителе, получателях и почте ДОЛЖНЫ быть отброшены, а все буферы и таблицы состояний очищены. Получатель ДОЛЖЕН отправить ответ "250 OK" на команду RSET без аргументов. Команда сброса может быть подана клиентом в любое время. Он фактически эквивалентен NOOP (то есть, если не имеет эффекта), если выдается сразу после EHLO, до того, как EHLO будет выпущен в сеансе, после того, как индикатор конца данных был отправлен и подтвержден, или непосредственно перед QUIT. Сервер SMTP НЕ ДОЛЖЕН закрывать соединение в результате получения RSET; это действие зарезервировано для ВЫХОДА (см. раздел 4.1.1.10).
Поскольку EHLO подразумевает некоторую дополнительную обработку и отклик со стороны сервера, RSET обычно будет более эффективным, чем повторное выполнение этой команды, даже если формальная семантика такая же.
Существуют обстоятельства, противоречащие цели данной спецификации, при которых SMTP-сервер может получить указание на то, что базовое TCP-соединение было закрыто или сброшено. Чтобы сохранить надежность почтовой системы, серверы SMTP ДОЛЖНЫ быть готовы к этому условию и ДОЛЖНЫ обрабатывать его так, как если бы выход был получен до того, как соединение исчезло.
Все вышеперечисленное произошло в течение секунды, поэтому проблем с тайм-аутами быть не должно.
В следующую секунду клиент отправил еще один RSET
но затем клиент ждал целых 10 секунд перед перезапуском с MAIL FROM:
, RCPT TO:
но на этот раз он продолжает и выдает DATA
команда и транзакция завершается (все в течение одной секунды, согласно журналам).
По сути, мне интересно, почему клиент SMTP прерывает свою транзакцию, выдавая RSET
команды вместо DATA
команда.
Ноты:
Я могу отредактировать вопрос, включив выдержку из файла журнала почты, но с -v
отладки, они очень подробны, и я не хочу перегружать людей потоком нерелевантных данных.
Я искал исходный код sSMTP, но не нашел упоминания о RSET
.
Мне было интересно, почему клиент SMTP прерывает свою транзакцию, выдавая команды RSET вместо команды DATA. Короткий ответ - нет; это признак того, что SMTP-соединения перехватываются антивирусным ПО.
Я включил опцию Debug в конфигурации для sSMTP, но мне потребовалось некоторое время, чтобы выяснить, как установить и настроить системный журнал в Cygwin, чтобы сообщения от процессов Cygwin регистрировались в /var/log/messages
вместо средства просмотра событий Windows.
Однако он зарегистрировал только первые MAIL FROM:
и RCPT TO:
команды; не было никаких указаний на то, что эти команды были отправлены более одного раза - или что sSMTP
когда-либо отправлял RSET
команда.
Как упоминалось в моем вопросе, я проверил исходный код sSMTP, но нет кода для отправки RSET
команда.
Пользователь masegaloeh предположил, что антивирусное программное обеспечение может изменять пакеты SMTP - и он был прав: я временно отключил Symantec Endpoint Protection на моем компьютере, и транзакции SMTP прошли нормально.
После повторного включения Symantec Endpoint Protection я отслеживал TCP-соединения с помощью TCPView полезность от Windows Sysinternals и я увидел, что Symantec ccSvcHst.exe
Процесс проксировал весь TCP-трафик с портом назначения 25.
Я подключился по telnet к порту 25 на почтовом сервере (netcat не работал должным образом, возможно, из-за перехвата Symantec), чтобы отправить тестовое письмо, используя вручную вводимые команды SMTP. В то же время у меня было открыто другое окно терминала с SSH-подключением к почтовому узлу во время работы sudo tail -F /var/log/maillog
для одновременного отслеживания того, что видит SMTP-сервер.
Перехват, выполняемый прокси-сервером Symantec, неуловим. С точки зрения почтового клиента очень мало признаков того, что он не обращается напрямую к SMTP-серверу. Большинство команд передаются на почтовый сервер по мере их отправки, и ответы соответствуют вашим ожиданиям. Так было, пока я не вошел в DATA
прокси-сервер Symantec начал что-то менять: он ответил:
354 Please start mail input.
Это кажется нормальным, но на самом деле ответ моего сервера Postfix должен был быть
354 End data with <CR><LF>.<CR><LF>
Кроме того: на самом деле он не прошел DATA
в Postfix до тех пор, пока я не завершу текст сообщения и не добавлю к нему QUIT
.
Примечание: пока я набирал содержимое тестового сообщения, прокси Symantec поддерживал соединение с сервером Postfix, выдавая NOOP
команды.
В своем вопросе я упомянул, что моей конечной целью было устранение неполадок в проприетарном (двоичном, с закрытым исходным кодом) почтовом клиенте, используемом моей организацией. Я обнаружил, что этот клиент действительно глючит: он отправляет неверный HELO
(без имени хоста), а затем просто сдается и ВЫХОДИТ после того, как SMTP-сервер вежливо проинформирует его о неправильном синтаксисе, хотя я настроил Postfix так, чтобы он не требовал HELO
(действительный или иной).
Я решил эту проблему, установив новый сервер с CentOS 7, который поставляется с достаточно свежей версией Postfix, чтобы я мог полностью отключить постфиксные проверки HELO (аналогично тому, как MS Exchange просто игнорирует недопустимые команды HELO).
Мне также было интересно узнать об общем использовании RSET
в транзакциях SMTP, и я нашел следующее в исходном RFC 821 для SMTP:
Эта команда указывает, что текущая почтовая транзакция должна быть прервана. Все сохраненные данные об отправителе, получателях и почте должны быть отброшены, а все буферы и таблицы состояний очищены. Получатель должен отправить ответ ОК.
RSET
будет использоваться, если окажется, что адрес электронной почты получателя принадлежит несуществующему пользователю. Следующая SMTP-транзакция является примером Сценарий прерванной транзакции SMTP.
R: 220 MIT-Multics.ARPA Simple Mail Transfer Service Ready
S: HELO ISI-VAXA.ARPA
R: 250 MIT-Multics.ARPA
S: MAIL FROM:<Smith@ISI-VAXA.ARPA>
R: 250 OK
S: RCPT TO:<Jones@MIT-Multics.ARPA>
R: 250 OK
S: RCPT TO:<Green@MIT-Multics.ARPA>
R: 550 No such user here
S: RSET
R: 250 OK
S: QUIT
R: 221 MIT-Multics.ARPA Service closing transmission channel
SMTP-клиенты могут использовать одно и то же SMTP-соединение для отправки нескольких сообщений одному и тому же месту назначения. Эта функция называется Кеширование SMTP-соединения пользователя Postfix. При использовании этой функции производительности Postfix отправляет RSET
перед каждым MAIL FROM
команда, чтобы убедиться, что соединение SMTP все еще можно использовать (см. Кэш подключений Postfix).