Имя хоста сервера почтового агента (MSA) с адреса электронной почты через C++
Я ищу общий способ получения имени хоста серверов отправки почты, используя только адрес электронной почты с помощью c/ C++, чтобы я мог подключиться к порту 587 и / или 465 отправки smtp.
Например, example@gmail.com -> smtp.gmail.com или gmail-smtp-msa.l.google.com.
В основном я посылаю некоторые конкретные письма от имени клиентов. В конце концов, похоже, что они отправили письмо. До сих пор я все еще использую простую карту имен доменов, например {gmail.com, smtp.gmail.com}, которую я расширяю всякий раз, когда это необходимо. Однако я бы очень хотел избежать этой карты и автоматизировать этот процесс.
Некоторое время назад я опубликовал сокеты C++ - имя хоста сервера smtp с адреса электронной почты, который должен был быть тем же вопросом, может быть, он был недостаточно ясен. В ответ мне посоветовали выполнить поиск DNS и запросить записи MX. Книга и реализация позже я заметил, что эти записи mx не подходят для отправки почты (не может подключиться к порту 587, только 25). Я проверил все другие типы, определенные в resolv.h, и проверил nslookup (который, вероятно, в любом случае использует resolv.h), и теперь я действительно думаю, что это невозможно сделать с помощью поиска DNS. Если кто-то хочет попробовать, проверьте nslookup -type=mx gmail.com
вы не увидите ничего-мса... вещи.
Сейчас я немного отчаялся, потому что очень хочу это сделать. Однако я также не хочу тратить на это недели. Я был бы также очень рад ответу, говорящему мне, что это невозможно в разумные сроки.
РЕДАКТИРОВАТЬ:
Следуя минимальному примеру, я только что сделал очень быстро с помощью подхода DNS MX и где я попытался подключиться к портам 25, 465, 587.
/*c++1z, Don't forget -lresolv*/
#include <resolv.h>
#include <string>
#include <map>
#include <sstream>
#include <cerrno>
#include <netdb.h>
#include <iostream>
#include <cstddef>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netinet/tcp.h>
//like assert but throws instead
#define CHECK(condition)\
if(!(condition)){\
std::ostringstream oss;\
oss << __FILE__ << ":" << __LINE__ << "\nerrno:" << errno << "\nh_errno:" << h_errno << std::endl;\
throw std::runtime_error(oss.str());\
}
//!
//! \brief mxRecords performs dns T_MX lookup
//! \param domain the domain part of the email e.g. example@gmail.com -> gmail.com
//! \return multimap of {priority, server hostname} pairs
//!
auto mxRecords(std::string const&domain)
->std::multimap<size_t/*priority*/,std::string/*server name*/>
{
std::basic_string<unsigned char> response(PACKETSZ,'0');
int response_size = res_search(domain.data(),C_IN,T_MX,response.data(),response.size());
CHECK(response_size != -1);
response.resize(response_size);
ns_msg handle;
CHECK(ns_initparse(response.data(),response.size(),&handle) != -1);
std::multimap<size_t,std::string> ret;
for(int i = 0; i < ns_msg_count(handle,ns_s_an); ++i)
{
ns_rr rr;
if(ns_parserr(&handle,ns_s_an,i,&rr) == -1)
continue;
if(ns_rr_type(rr) != ns_t_mx)
continue;
std::string exp_dn(MAXDNAME,0);
if(ns_name_uncompress(ns_msg_base(handle),ns_msg_end(handle),ns_rr_rdata(rr)+NS_INT16SZ,exp_dn.data(),exp_dn.size()) == -1)
continue;
ret.emplace(ns_get16(ns_rr_rdata(rr)),exp_dn.data());
}
return ret;
}
bool __connect(std::string const&host, std::string const&port)
{
addrinfo const __req{0,AF_UNSPEC,SOCK_STREAM,IPPROTO_TCP,0,nullptr,nullptr,nullptr};
addrinfo *__pai(nullptr);
if(getaddrinfo(host.data(),port.data(),&__req,&__pai) != 0)
return false;
for(addrinfo *iter = __pai; iter != nullptr; iter = iter->ai_next)
{
int fd = socket(iter->ai_family, iter->ai_socktype, iter->ai_protocol);
if(fd == -1)
continue;
int retries = 1;
setsockopt(fd, IPPROTO_TCP, TCP_SYNCNT, &retries, sizeof(retries));
if(::connect(fd, iter->ai_addr, iter->ai_addrlen) == -1)
{
close(fd);
continue;
}
freeaddrinfo(__pai);
close(fd);
return true;
}
freeaddrinfo(__pai);
return false;
}
int main()
{
std::string domain = "gmail.com";
//get mx records and try to connect for port 25,465,587
auto mx_records = mxRecords(domain);
std::cout << "----------MX RECORDS <priority|host|port25|port465|port587> for " << domain << std::endl;
for(auto iter = mx_records.begin(); iter != mx_records.end(); ++iter)
{
std::cout << iter->first << " | " << iter->second;
//try port 25|port 465 | port 587
for(auto port : {"25","465","587"})
{
if(__connect(iter->second,port))
std::cout << " | y";
else
std::cout << " | n";
}
std::cout << std::endl;
}
return 0;
}
дает в качестве выхода
----------MX RECORDS <priority|host|port25|port465|port587> for gmail.com
5 | gmail-smtp-in.l.google.com | y | n | n
10 | alt1.gmail-smtp-in.l.google.com | y | n | n
20 | alt2.gmail-smtp-in.l.google.com | y | n | n
30 | alt3.gmail-smtp-in.l.google.com | y | n | n
40 | alt4.gmail-smtp-in.l.google.com | y | n | n
Обратите внимание на " внутри " внутри этих имен хостов. Я сделал запрос DNS для cname smtp.gmail.com, и он дает мне gmail-smtp-msa.l.google.com, так что "msa", который открыт для портов 587/465. Я действительно думаю, что эти записи mx в настоящее время хороши только для порта 25, а не для отправки почты.
РЕДАКТИРОВАТЬ № 2:
Существует большая путаница с портами 25, 465, 587 в Интернете. Просто, чтобы прояснить ситуацию для меня, эти порты не отличаются по ssl/tls априори. Порт 25, например, когда вы хотите отправить электронное письмо с example@yourDomain.com на example@gmail.com. Порт 587, например, когда вы хотите отправить электронное письмо с example1@gmail.com на example2@gmail.com. Наконец, порт 465[устарел] точно такой же, как и 587, с добавлением, что он начинается уже с ssl/tls. Пожалуйста, поправьте меня, если я ошибаюсь, но я уверен, что нет! После логов 3 EHLO запускается из моего реального приложения. Сначала используется имя хоста mx-записи и порт 25, затем - "smtp.gmail.com" и порт 587, а последний - "smtp.gmail.com" и порт 465. Обратите внимание на разницу для 587/465 с параметром команды AUTH после безопасное соединение и 25 без опции AUTH. Таким образом, при отправке электронных писем через порт 25 получатель получает уведомление о том, что отправитель не проверен, и вы, как правило, вскоре получаете блокировку (СПАМ). В любом случае мне нужна эта команда AUTH, приложение полностью легально, нет спама и т. Д., И именно поэтому мне нужны SMTP-серверы с открытым портом 587, и я не думаю, что эти MX-серверы являются правильными.
ПОРТ-25
S:220 mx.google.com ESMTP ############### - gsmtp
C:EHLO ###############
S:250-mx.google.com at your service, ###############
250-SIZE 157286400
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
C:STARTTLS
S:220 2.0.0 Ready to start TLS
C:EHLO ###############
S:250-mx.google.com at your service, ###############
250-SIZE 157286400
250-8BITMIME
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
C:QUIT
S:221 2.0.0 closing connection ############### - gsmtp
ПОРТ-587
S:220 smtp.gmail.com ESMTP ############### - gsmtp
C:EHLO ###############
S:250-smtp.gmail.com at your service, ###############
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
C:STARTTLS
S:220 2.0.0 Ready to start TLS
C:EHLO ###############
S:250-smtp.gmail.com at your service, ###############
250-SIZE 35882577
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
C:QUIT
S:221 2.0.0 closing connection ############### - gsmtp
ПОРТ-465
S:220 smtp.gmail.com ESMTP ############### - gsmtp
C:EHLO ###############
S:250-smtp.gmail.com at your service, ###############
250-SIZE 35882577
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
C:QUIT
S:221 2.0.0 closing connection ############### - gsmtp
1 ответ
Технически это не обязательно должно поддерживаться, но согласно RFC 6186 сек. 3.1 вы должны запросить записи SRV для _submission
хозяин в _tcp
субдомен, вот так:
$ nslookup -type=srv _submission._tcp.gmail.com
Server: google-public-dns-a.google.com
Address: 8.8.8.8
Non-authoritative answer:
_submission._tcp.gmail.com SRV service location:
priority = 5
weight = 0
port = 587
svr hostname = smtp.gmail.com
Некоторые вещи, которые вы должны иметь в виду:
- В некотором смысле это "удобная функция". Если у вас есть адрес электронной почты
myname@acme.com
, тогда acme.com может (или не может) выставлять эти записи RFC 6186 в_tcp.acme.com
разрешить программе почтового клиента своих пользователей автоматически обнаруживать такие службы, как POP3, IMAP и т. д. - Могут быть особые ограничения, налагаемые на подключение к домену MSA. Например, сервисы могут просто отказаться от соединений с компьютеров за пределами
xyz.com
внутренняя сеть, или требуется внутренне доверенный сертификат, и так далее. MSA предназначен для взаимодействия с почтовым клиентом, MUA, а не для обычной доставки почты.