
Отправка писем через SMTP-сервер на Ассемблере

Отправка писем через SMTP-сервер на Ассемблере
Простое обращение к стороннему серверу мы недавно попробовали, теперь можно сделать что-нибудь поинтереснее и посложнее. В этой статье я расскажу, как отправлять почту через SMTP-сервер с авторизацией с помощью Ассемблера.
Перед тем, как отправлять почту, надо указать SMTP-сервер, порт подключения (обычно 25), логин и пароль для доступа к ящику отправителя, адрес получателя и текст письма. В статье они все заполнены тестовыми значениями, в реальной программе все должно быть заполнено корректными данными. Само тело письма, скорее всего, тоже будет формироваться динамически, возможно даже с аттачами, но это уже выходит за рамки статьи. Проще всего открыть заголовки любого электронного письма в вашем почтовом клиенте и посмотреть, как и из каких частей оно складывается. Команды для передачи SMTP-серверу менять не надо, они остаются как есть.
Code (Assembler) : Убрать нумерацию
- ; Адрес SMTP-сервера
- smtp db 'mail.example.test',0
- ; Порт
- port db 25
- ; Логин от почтового ящика
- login db 'email@example.test',0
- ; Пароль от почтового ящика
- passw db 'supa-dupa-password',0
- ; От кого
- sfrom db 'email@example.test',0
- ; Кому
- sto db 'my_dear_friend@another.domain',0
- ; Тело письма
- message db 'To: My Dear Friend <my_dear_friend@another.domain>',13,10
- db 'Subject: Test message',13,10
- db 'Hello from FASM!',0
- ; Данные для подключения к серверу
- wsadata WSADATA
- saddr sockaddr_in
- hSocket dd ?
- ; Массивы для принятых и отправленных данных
- s_data rb 100h
- r_data rb 100h
- ; Вспомогательный буфер для разных операций
- buff rb 100h
- IPPROTO_TCP = 6
- SOCKET_ERROR = -1
Code (Assembler) : Убрать нумерацию
- ; Команды для передачи SMTP-серверу
- szEHLO db 'EHLO localhost',13,10,0
- szAUTH db 'AUTH LOGIN',13,10,0
- szLINE db '%s',13,10,0
- szFROM db 'MAIL FROM:<%s>',13,10,0
- szTO db 'RCPT TO:<%s>',13,10,0
- szDATA db 'DATA',13,10,0
- szEOD db 13,10,'.',13,10,0
- szQUIT db 'QUIT',13,10,0
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------------
- ; Отправка письма через SMTP-сервер
- ;----------------------------------------------------------------
- invoke WSAStartup,0101h,wsadata
- invoke gethostbyname,smtp
- or eax,eax
- jz loc_ret
- mov eax,[eax+hostent.h_addr_list]
- mov eax,[eax]
- mov eax,[eax]
- @@:
- mov [saddr.sin_addr],eax
- mov al,00
- mov ah,[port]
- mov [saddr.sin_port],ax
- mov [saddr.sin_family],AF_INET
- ; Создать сокет
- invoke socket,AF_INET,SOCK_STREAM,IPPROTO_TCP
- cmp eax,SOCKET_ERROR
- jne @f
- invoke WSACleanup
- jmp loc_ret
- @@:
- mov [hSocket], eax
- ; Соединение с SMTP-сервером
- invoke connect,[hSocket],saddr,sizeof.sockaddr_in
- cmp eax,SOCKET_ERROR
- jne @f
- invoke closesocket,[hSocket]
- invoke WSACleanup
- jmp loc_ret
- @@:
- invoke RtlZeroMemory,r_data,100h
- ; Получить данные после соединения
- invoke recv,[hSocket],r_data,100h,0
- cmp eax,SOCKET_ERROR
- je loc_close_socket
- invoke lstrcat,szLog,r_data
- ; После соединения сервер может вернуть
- ; код 2xx или не вернуть ничего
- cmp byte [r_data],0
- je @f
- cmp byte [r_data],'2'
- jne loc_close_socket
- @@:
- ; EHLO
- stdcall send_string,szEHLO
- ; Проверить ответ сервера
- or eax,eax
- jz loc_close_socket
- cmp byte [r_data],'5'
- je loc_close_smtp
- cmp byte [r_data],'4'
- je loc_close_smtp
- ; Если не задан логин и/или пароль, то
- ; считаем, что сервер не использует авторизацию
- cmp byte [login],0
- je loc_no_auth
- cmp byte [passw],0
- je loc_no_auth
- ; AUTH LOGIN
- stdcall send_string,szAUTH
- ; Проверить ответ сервера
- or eax,eax
- jz loc_close_socket
- cmp byte [r_data],'5'
- je loc_close_smtp
- cmp byte [r_data],'4'
- je loc_close_smtp
- ; -> login в base64
- stdcall base64_encode,login,buff
- invoke wsprintf,s_data,szLINE,buff
- add esp,12
- stdcall send_string,s_data
- ; Проверить ответ сервера
- or eax,eax
- jz loc_close_socket
- cmp byte [r_data],'5'
- je loc_close_smtp
- cmp byte [r_data],'4'
- je loc_close_smtp
- ; -> password в base64
- stdcall base64_encode,passw,buff
- invoke wsprintf,s_data,szLINE,buff
- add esp,12
- stdcall send_string,s_data
- ; Проверить ответ сервера
- or eax,eax
- jz loc_close_socket
- cmp byte [r_data],'2'
- jne loc_close_smtp
- loc_no_auth:
- ; MAIL FROM
- invoke wsprintf,s_data,szFROM,sfrom
- add esp,12
- stdcall send_string,s_data
- ; Проверить ответ сервера
- or eax,eax
- jz loc_close_socket
- cmp byte [r_data],'2'
- jne loc_close_smtp
- ; RCPT TO
- ; Если получателей несколько, то эта команда
- ; передается для каждого адреса email отдельно
- invoke wsprintf,s_data,szTO,sto
- add esp,12
- stdcall send_string,s_data
- ; Проверить ответ сервера
- or eax,eax
- jz loc_close_socket
- cmp byte [r_data],'2'
- jne loc_close_smtp
- ; DATA
- stdcall send_string,szDATA
- ; Проверить ответ сервера
- or eax,eax
- jz loc_close_socket
- cmp byte [r_data],'5'
- je loc_close_smtp
- cmp byte [r_data],'4'
- je loc_close_smtp
- ; -> message
- invoke lstrcat,szLog,message
- invoke lstrlen,message
- invoke send,[hSocket],message,eax,0
- cmp eax,SOCKET_ERROR
- je loc_close_socket
- ; EOD
- stdcall send_string,szEOD
- ; Проверить ответ сервера
- or eax,eax
- jz loc_close_socket
- cmp byte [r_data],'2'
- jne loc_close_smtp
- loc_close_smtp:
- ; QUIT
- stdcall send_string,szQUIT
- ; Проверить ответ сервера
- or eax,eax
- jz loc_close_socket
- cmp byte [r_data],'2'
- jne loc_close_socket
- ; Сообщение успешно отправлено
- loc_close_socket:
- invoke closesocket,[hSocket]
- invoke WSACleanup
Code (Assembler) : Убрать нумерацию
- proc send_string szStr:DWORD
- locals
- result dd ?
- endl
- pusha
- ; По умолчанию результат - ошибка
- mov [result],0
- invoke RtlZeroMemory,r_data,100h
- ; Добавить в лог передаваемые данные
- invoke lstrcat,szLog,[szStr]
- invoke lstrlen,[szStr]
- invoke send, [hSocket], [szStr], eax, 0
- cmp eax,SOCKET_ERROR
- je @f
- invoke recv, [hSocket], r_data, 100h, 0
- cmp eax,SOCKET_ERROR
- je @f
- ; Добавить в лог полученный ответ
- invoke lstrcat,szLog,r_data
- ; Данные отправлены и получены успешно
- mov [result],1
- @@:
- popa
- ; Вернуть результат
- mov eax,[result]
- ret
- endp
В приложении исходный код программы, которая отправляет письмо через SMTP-сервер. Готового exe-шника нет, так как почтовый сервер, логины и пароли вы должны указать свои. После отправки письма будет показан полный лог "общения" с SMTP-сервером.
Просмотров: 3820 | Комментариев: 9

Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье

ManHunter
(20.10.2022 в 14:07):
Охуенно содержательный каммент, браво.

sour11
(20.10.2022 в 14:05):
WSAEACCES (0000271D)

ManHunter
(28.05.2020 в 15:36):
Проблема портировать что ли? Работы на полчаса с перекуром.

Александр
(28.05.2020 в 15:33):
Есть эти исходники для Masm32 ?

Вячеслав
(21.04.2019 в 11:36):
Что-нибудь получилось с отправкой через защищенное соединение?

zdm
(16.06.2016 в 21:42):
Да уж. Было бы очень интересно посмотреть на логику программы при всяких там STARTTLS и различных схемах авторизации кроме PLAIN и LOGIN.

ManHunter
(13.04.2016 в 08:19):
wetal, корпоративная почта, например. Или почта на виртуальном хостинге.
Надо будет на досуге покурить мануалы по отправке через защищенное соединение.
Надо будет на досуге покурить мануалы по отправке через защищенное соединение.

wetal
(13.04.2016 в 07:35):
Опоздали Вы с кодом лет эдак на пять!
Даже не знаю, на какой реально работающей почте это можно проверить? Сейчас обычно защита соединения SSL.
Даже не знаю, на какой реально работающей почте это можно проверить? Сейчас обычно защита соединения SSL.

Добавить комментарий
Заполните форму для добавления комментария

push 101h
call [WSAStartup]
push IPPROTO_IP
push SOCK_STREAM
push AF_INET
call [socket]
mov [hsock],eax
push 25
call [htons]
mov word [addr.sin_port],ax
mov word [addr.sin_family],AF_INET
push hostsmtp
call [gethostbyname]
mov eax,[eax+12]
mov eax,[eax]
mov eax,[eax]
mov dword [addr.sin_addr],eax
push sizeof.sockaddr_in
push addr
push [hsock]
call [connect]
WSAEACCES (0000271D)