Изучаем SMTP протокол для отправки писем (примеры программ на WinSock API и INDY)
Описание протокола SMTP и примеры работы с ним.
На написание этой статьи меня подтолкнул тот факт, что многие пишут программы для отправки почты с использованием каких либо компонентов, но на самом деле даже не представляют как происходит обмен данными между почтовым клиентом и сервером. Сегодня мы рассмотрим, как отправить почту, используя протокол SMTP (Simple Mail Transfer Protocol). Для начала мы выполним ручную отправку почты с помощью Telnet-клиента putty, и наконец, то узнаем, как происходит процесс отправки почты. Затем напишем программу на низком уровне с использованием WinSock API. И напоследок разберем возможные ошибки при работе с SMTP протоколом, а также устранение этих ошибок при работе со сторонними компонентами.
Сразу хочется отметить, что SMTP это текстовый протокол и общение с сервером происходит следующим образом:
Клиент передает на сервер строку команда<пробел>параметры<перевод строки>. Сервер отвечает на каждую команду строкой, содержащей код ответа и текстовое сообщение, отделенное пробелом. Код ответа — число от 100 до 999, представленное в виде строки, трактующийся следующим образом:
2ХХ — команда успешно выполнена
3XX — ожидаются дополнительные данные от клиента
4ХХ — временная ошибка, клиент должен произвести следующую попытку через некоторое время
5ХХ — неустранимая ошибка
Текстовая часть ответа носит справочный характер и предназначена для человека, а не программы.
Для отправки письма нам нужно подключиться к почтовому серверу. Работать будем с mail.ru, я зарегал специально мыло [Ссылки могут видеть только зарегистрированные пользователи. ] для статьи. Запускаем putty и настраиваем его на подключение к почтовому серверу.
[Ссылки могут видеть только зарегистрированные пользователи. ]
В поле “Host Name (or IP address)” вводим адрес SMTP сервера, раз мы решили использовать сервер mail.ru, то пишем smtp.mail.ru. В поле “Port” указываем порт сервера, по умолчанию это 25 порт, в группе переключателей “ Connection type: ” выбираем тип соединения “Telnet”, в принципе все готово, теперь нажимаем на кнопку “Open” и подключаемся к серверу. Как видно на скрине, сервер уведомил нас об успешном подключении кодом 220.
[Ссылки могут видеть только зарегистрированные пользователи. ]
Теперь нам нужно представиться, для этого используется команда “HELO имя”, в качестве имени принято использовать имя своего компьютера, если подключение осуществляется с локального компа, или имя/IP-адрес своего хоста, если подключение осуществляется с веб-сервера каким либо скриптом. Имя компьютера можно узнать, нажав правой кнопкой на значке “Мой компьютер”, у меня оно “TurPb”
[Ссылки могут видеть только зарегистрированные пользователи. ]
[Ссылки могут видеть только зарегистрированные пользователи. ]
Код:
HELO TurPb
При успешном выполнении команды, сервер ответит нам кодом 250.
Далее нам нужно авторизоваться, для этого используем команду:
Код:
AUTH LOGIN
сервер ответил нам кодом 334 и непонятным сообщением (“334 VXNlcm5hbWU6”). Как мы уже знаем коды 3XX означают, что требуются дополнительные параметры, но что, же требует от нас сервер? Оказывается это непонятное сообщение закодировано в Base64.Ррасшифруем текст, для этой цели я написал простенький конвертер “Base64Converter”, позволяющий зашифровывать и расшифровывать сообщения в Base64. После расшифровки мы видим, что это не что иное как “Username:”,
[Ссылки могут видеть только зарегистрированные пользователи. ]
значит сервер просит нас сказать ему логин. Ответ нужно передавать тоже в кодировке Base64, в качестве логина будем использовать [Ссылки могут видеть только зарегистрированные пользователи. ] (это часть е мейла до @, в качестве логина так же можно использовать все мыло целиком [Ссылки могут видеть только зарегистрированные пользователи. ]), закодируем его в Base64
[Ссылки могут видеть только зарегистрированные пользователи. ]
и пошлем серверу:
Код:
d3d3LmdjZHRlYW0uN2lsLnJ1
Далее сервер как все уже догадались, попросит у нас пароль “334 UGFzc3dvcmQ6”, кодируем наш пароль “qwerty” в Base64 и посылаем серверу:
Код:
cXdlcnR5
на что он довольно ответит “ 235 Authentication succeeded”.
После успешной авторизации перейдем к отправке письма. Отправлять будем на ящик [Ссылки могут видеть только зарегистрированные пользователи. ] я его зарегистрировал в сервисе [Ссылки могут видеть только зарегистрированные пользователи. ] что бы долго не париться. Сообщим серверу, кто является отправителем письма:
Код:
MAIL FROM:<[Ссылки могут видеть только зарегистрированные пользователи. ]>
Сервер ответит нам кодом “250 Accepted” что снова говорит об успешном выполнении команды.
Затем укажем получателя письма:
Код:
RCPT TO:<[Ссылки могут видеть только зарегистрированные пользователи. ]>
Сервер ответит нам кодом 250. После этого сформируем само тело письма. Для этого используем команду:
Код:
DATA
Сервер говорит нам “354 Enter message, ending with "." on a line by itself”. Далее вводим заголовок, кодировку и сам текст сообщения:
Код:
From: <[Ссылки могут видеть только зарегистрированные пользователи. ]>
To: <[Ссылки могут видеть только зарегистрированные пользователи. ]>
Subject: Test message
MIME-Version: 1.0
Content-Type: text/plain; charset="windows-1251"
Привет, Тигрь! Это тестовое сообщение для статьи. =)
.
Я думаю тут все понятно. Сообщение заканчивается точкой “.” на новой строке, именно так сервер определит что сообщение закончилось и его следует отправлять.
Если все прошло успешно то сервер в который раз ответит нам командой 250.
После отправки письма необходимо завершить сессию:
Код:
QUIT
[Ссылки могут видеть только зарегистрированные пользователи. ]
Проверим почту и убедимся что сообщение пришло:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Вот в принципе и все. Именно таким образом передают почту различные компоненты, например INDY.
Вот мы и подошли ко второму этапу, написания программы на WinSock API.
WinSock это сетевая библиотека Windows, и находится в папке C:\Windows\System32 под именем WINSOCK.DLL. Я не буду подробно разжевывать работу с WinSock API, а лишь приведу функции которые нам понадобятся и их описание, исходники будут приложены к статье и в них не трудно будет разобраться. Для того что бы Delphi понимал что мы используем WinSock API то нужно подключить модуль winsock. В нем содержатся необходимые объявления импортируемых функций WinSock API и базовые структуры данных.
Функция с которой начинается написание любой программы использующей WinSock API:
Код:
function WSAStartup(wVersionRequired: word; var WSData: TWSAData): Integer; stdcall;
Функция инициализирует библиотеку WinSock. Она содержит два параметра:
wVersionRequested – входящий параметр - версия инициализируемой библиотеки.
WSAData – исходящий параметр - указатель на структуру WSAData. После выполнения функции в эту структуру запишется информация о сетевой библиотеке.
В случае успешного выполнения функция возвращает значение ноль, в противном случае код ошибки.
Для получения кодов ошибок в WinSock API служит функция WSAGetLastError():
Код:
function WSAGetLastError: Integer; stdcall;
Ей не нужно передавать какие-либо параметры, после вызова она возвращает код последней возникшей ошибки при работе с сетевыми функциями.
Код:
function socket(af, Struct, protocol: Integer): TSocket; stdcall;
Функция создает сокет. Входящие параметры:
af - спецификация семейства сокетов (PF_INET, PF_IPX и др.)
type - тип создаваемого сокета. Может быть SOCK_STREAM (для протокола TCP/IP) и SOCK_DGRAM (UDP).
protocol - специфический протокол, который будет использоваться сокетом
Если функция выполнена без ошибок, она возвращает дескриптор на новый сокет, если ошибки есть, возвращается INVALID_SOCKET.
Код:
function connect(s: TSocket; var name: TSockAddr; namelen: Integer): Integer; stdcall;
Функция соединения для клиента. Параметры:
s - дескриптор, идентифицирующий сокет (это значение возвращает функция socket)
name - структура SockAddr, содержащая данные, необходимые для подключения (протокол, адрес удаленного компьютера, порт)
namelen - размер структуры типа TSockAddr (можно получить, используя функцию sizeof)
В случае успешного коннекта функция возвращает значение ноль. Если коннект не удался, возвращаемое значение - SOCKET_ERROR (код ошибки можно получить, используя функцию WSAGetLastError).
Две функции для отправки и приема данных в/из сокета соответственно:
Код:
function send(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
function recv(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
Параметры у них одинаковые:
s - сокет, на который нужно отправить (или принять) данные.
buf - буфер с данными для отправки (приема).
len - размер передаваемых (принимаемых) данных.
flags - флаги, отвечающие за метод отправки (приема).
Выполнившись, функция вернет фактическое количество отправленных/принятых байт.
Код:
function CloseSocket(s:TSocket):integer;stdcall;
Эта функция служит для закрытия сокета, в качестве параметра TSocket, передается дескриптор, идентифицирующий сокет.
function WSACleanup: Integer;
Функция сообщает ОС, что приложение более не использует WinSock. Должна быть вызвана перед завершением приложения. Не имеет параметров. Если выполнена успешно - возвращает ноль и прекращает использования сокетов Windows. Если есть ошибка во время выполнения возвращает код ошибки.
Это основные функции для работы с WinSock. Исходный код программы находится в архиве приложенном к статье.
Вот мы и подошли к завершающему этапу, рассмотрения возможных ошибок при работе с SMTP протоколом.
Как было написано в начале если почта отправляется с локального компьютера то в команде HELO передается имя нашего компьютера. При тестировании протокола мной было замечено что сервер не воспринимает русские имена, и выдает ошибку.
[Ссылки могут видеть только зарегистрированные пользователи. ]
Тоже самое мы видим в моей программе на WinSock API. Я сделал функцию отправки собственного имени в команде HELO.
[Ссылки могут видеть только зарегистрированные пользователи. ]
В программах написанных с использованием компонентов, например Indy вываливается исключение дебагера.
[Ссылки могут видеть только зарегистрированные пользователи. ]
А в самой программе:
[Ссылки могут видеть только зарегистрированные пользователи. ]
Для того что бы эта ошибка не появлялась, IdSMTP.Connect(); нужно заключать в конструкцию try .. except. Как это сделано у меня:
Код:
try
IdSMTP.Connect();
MemoLog.Lines.Add('Подключение к серверу');
except
MemoLog.Lines.Add('Неверный аргумент HELO');
end;
Ошибка то появляться не будет но письмо не отправится так как сервер не принял русского имени компьютера. Это можно исправить отправляя серверу не имя компьютера а принудительно установленное любое английское слово (так как в качестве имени компьютера мы можем указать любую информацию, ведь SMTP сервер не сможет этого проверить). В компоненте Indy делается это к примеру так:
Код:
IdSMTP.HeloName := ‘TurPb’; // HELO
Теперь сервер не будет ругаться при отправке команды HELO.
Вот в принципе все что я хотел рассказать. Хотелось бы сказать что когда пишите какие то программы старайтесь разбираться как это устроено “внутри” и как все работает.
Исходные коды SMTP клиента c использованием WinSock API
Исходные коды SMTP клиента с использованием компонентов Indy
Исходные коды конвертера Base64
Telnet-клиент putty
[Ссылки могут видеть только зарегистрированные пользователи. ]
________________ Продаю приват читы для AION Absolute, desteny 3.5, Legend, Cataclysm, Ru, EU, NA Продаю многооконку на R2 Пишу читы на заказ под любые игры. Предложения в ЛС. Все мои читы/программы/статьи тут:http://zhyk.ru/forum/showpost.php?p=38501&postcount=21
Последний раз редактировалось Тигрь; 11.08.2010 в 14:48.
Re: Изучаем SMTP протокол для отправки писем (примеры программ на WinSock API и INDY)
какое отношение имеет INDY к данной теме, нет ни ссылки. нет ни описания куда класть скаченные компоненты.
Хоть и статья полезная, и все говорят про INDY - НИ СЛОВА НЕ СКАЗАНО ОТКУДА ЕГО СКАЧАТЬ и КУДА ПОЛОЖИТЬ в папку с DELPHI
Re: Изучаем SMTP протокол для отправки писем (примеры программ на WinSock API и INDY)
Статья не по использованию инди, а по работе протокола SMTP. Инди входит в стандартную поставку делфи. И в соседних темах полно описаний как работать с этим компонентом. Я же описал протокол, и в дабавок привел возможнве ошибки при работе с ним, и в качестве примера объясния возможность возникновения ошибки на инди.
________________ Продаю приват читы для AION Absolute, desteny 3.5, Legend, Cataclysm, Ru, EU, NA Продаю многооконку на R2 Пишу читы на заказ под любые игры. Предложения в ЛС. Все мои читы/программы/статьи тут:http://zhyk.ru/forum/showpost.php?p=38501&postcount=21
Re: Изучаем SMTP протокол для отправки писем (примеры программ на WinSock API и INDY)
Цитата:
Сообщение от Тигрь
Статья не по использованию инди, а по работе протокола SMTP. Инди входит в стандартную поставку делфи. И в соседних темах полно описаний как работать с этим компонентом. Я же описал протокол, и в дабавок привел возможнве ошибки при работе с ним, и в качестве примера объясния возможность возникновения ошибки на инди.
Я скачал портабл делфи, так как полную не нашёл. Там мне ошибки выдает типо отсутсвуют библиотеки... Как это понимать
Re: Изучаем SMTP протокол для отправки писем (примеры программ на WinSock API и INDY)
Портабл версии урезанные, в них оставлены только стандартные компаненты, такие как кнопки, меню, панели. Качай полную версию, посмотри на торентах, я не буду объяснять где скачать софт.
________________ Продаю приват читы для AION Absolute, desteny 3.5, Legend, Cataclysm, Ru, EU, NA Продаю многооконку на R2 Пишу читы на заказ под любые игры. Предложения в ЛС. Все мои читы/программы/статьи тут:http://zhyk.ru/forum/showpost.php?p=38501&postcount=21
Re: Изучаем SMTP протокол для отправки писем (примеры программ на WinSock API и INDY)
Я хочу с помощью NeoBook 5.5.4 создать программу для взлома маила. Это будет прога типо бесплатных стикерев....жертва вносит нужные данные нажимает ОТПРАВИТЬ СТИКЕР, а данные приходят мне на мыло! Можно ли так сделать? если да ,то как? Опишите подробно! Please!!!!!!!!!!